package ms.jasim.framework;

import javax.swing.DefaultComboBoxModel;

import ms.jasim.framework.JEventInstance.JEventInstanceProperty;
import ms.spm.IAppContext;
import ms.utils.NamedList;

import com.l2fprod.common.beans.editor.ComboBoxPropertyEditor;
import com.l2fprod.common.propertysheet.Property;

public abstract class JEventType {

	public static final String REPLAN_OPT = "ReplanOpt";
	public static final String TIME = "Time";
	
	public enum EventTimeType {
		ABSOLUTE, RELATED
	}
	
	public enum EventTimeAction {
		SATISFIES, PASSES
	}
	
	public static class EventTime {
		private int absTime;
		private EventTimeType type = EventTimeType.ABSOLUTE; 
		private EventTimeAction action = EventTimeAction.SATISFIES;
		private PddlModel.Goal goal;
		private PddlModel.Actor actor;
		
		public int getAbsTime() {
			return absTime;
		}

		public void setAbsTime(int absTime) {
			this.absTime = absTime;
		}

		public EventTimeType getType() {
			return type;
		}

		public void setType(EventTimeType type) {
			this.type = type;
		}

		public EventTimeAction getAction() {
			return action;
		}

		public void setAction(EventTimeAction action) {
			this.action = action;
		}

		public PddlModel.Goal getGoal() {
			return goal;
		}

		public void setGoal(PddlModel.Goal goal) {
			this.goal = goal;
		}

		public PddlModel.Actor getActor() {
			return actor;
		}

		public void setActor(PddlModel.Actor actor) {
			this.actor = actor;
		}
	
		@Override
		public String toString() {
			if (type == EventTimeType.ABSOLUTE)
				return "" + absTime;
			String actionName = action == EventTimeAction.SATISFIES ? "satisfying" : "passing";
			return String.format("+%d when %s starts %s %s", absTime, actor != null ? actor.getName() : "someone", actionName, goal != null ? goal.getName() : "any goal");
		}
	}

	public static class EventReplanOption {
		private int numberOfSolutions, maxProcessTime;
		private boolean replan = true;
		private boolean onceTimeEvent = true;

		public void setMaxProcessTime(int maxProcessTime) {
			this.maxProcessTime = maxProcessTime;
		}

		public int getMaxProcessTime() {
			return maxProcessTime;
		}

		public void setNumberOfSolutions(int numberOfSolutions) {
			this.numberOfSolutions = numberOfSolutions;
		}

		public int getNumberOfSolutions() {
			return numberOfSolutions;
		}

		public void setReplan(boolean replan) {
			this.replan = replan;
		}

		public boolean isReplan() {
			return replan;
		}
		
		@Override
		public String toString() {
			return replan ? String.format("%d solutions/%d ms", numberOfSolutions, maxProcessTime) : "not applicable";
		}

		
		public void setOnceTimeEvent(boolean onceTimeEvent) {
			this.onceTimeEvent = onceTimeEvent;
		}

		
		public boolean isOnceTimeEvent() {
			return onceTimeEvent;
		}
	}
	
	public class Parameter {
		private String name, text, description;
		private ParameterType type;
		private Object defaultValue;

		public Parameter(String name, String displayText, ParameterType type) {
			this.name = name;
			this.type = type;
			text = displayText;
		}
		
		public Parameter(String name, String displayText, ParameterType type, Object defaultValue) {
			this(name, displayText, type);
			this.defaultValue = defaultValue;
		}

		public String getName() {
			return name;
		}

		public String getText() {
			return text != null && text.length() > 0 ? text : name;
		}

		public String getDescription() {
			return description;
		}

		public void setDescription(String description) {
			this.description = description;
		}

		public ParameterType getType() {
			return type;
		}

		
		public void setDefaultValue(Object defaultValue) {
			this.defaultValue = defaultValue;
		}

		public Object getDefaultValue() {
			return defaultValue;
		}

		public Property createProperty(Object host) {
			JEventInstance.JEventInstanceProperty result = new JEventInstance.JEventInstanceProperty(this);
			type.createSubProperties(result);
			return result;
		}

	}

	public static abstract class ParameterType {

		public final static ParameterType Int = new IntParameterType();
		public final static ParameterType Goal = new GoalParameterType();
		public final static ParameterType Time = new TimeParameterType();
		public final static ParameterType Actor = new ActorParameterType();
		public final static ParameterType Bool = new BooleanParameterType();
		public final static ParameterType ReplanOption = new ReplanOptionParameterType();

		public String convertToString(IAppContext context, Object value) {
			return value != null ? value.toString() : "[null]";
		}

		public void createSubProperties(JEventInstanceProperty result) {
			// do nothing
		}

		public <T> T convertFromString(IAppContext context, String value) throws Exception {
			return null;
		}

		@SuppressWarnings("unchecked")
		public abstract Class getBaseType();
	}

	static class IntParameterType extends ParameterType {

		@SuppressWarnings("unchecked")
		@Override
		public <T> T convertFromString(IAppContext context, String value) throws Exception {
			return (T) new Integer(Integer.parseInt(value));
		}

		@SuppressWarnings("unchecked")
		@Override
		public Class getBaseType() {
			return int.class;
		}
	}

	static class BooleanParameterType extends ParameterType {

		@SuppressWarnings("unchecked")
		@Override
		public Class getBaseType() {
			return boolean.class;
		}

	}
	
	static class GoalParameterType extends ParameterType {

		@SuppressWarnings("unchecked")
		@Override
		public <T> T convertFromString(IAppContext context, String arg0) throws Exception {
			PddlModel model = context != null ? context.getService(PddlModel.class) : null;
			return (T) (model != null ? model.Goals.get(arg0) : null);
		}

		@Override
		public String convertToString(IAppContext context, Object arg0) {
			return arg0 instanceof PddlModel.Goal ? ((PddlModel.Goal) arg0).getName() : "";
		}

		@SuppressWarnings("unchecked")
		@Override
		public Class getBaseType() {
			return PddlModel.Goal.class;
		}
	}

	public static class NamedListPropertyEditor<E> extends ComboBoxPropertyEditor {

		public NamedListPropertyEditor(NamedList<E> objects) {
			NamedListComboBoxModel<E> model = new NamedListComboBoxModel<E>(objects);
			// model.insertElementAt(null, 0);
			setComboBoxModel(model);
		}
	}
	
	public static class EnumPropertyEditor extends ComboBoxPropertyEditor {
		
		@SuppressWarnings("unchecked")
		private Class enumType;

		@SuppressWarnings("unchecked")
		public EnumPropertyEditor(Class enumType, Object[] enumValues) {
			setComboBoxModel(new DefaultComboBoxModel(enumValues));
			this.enumType = enumType;
		}
		
		@Override
		public String getAsText() {
			return getValue().toString();
		}
		
		@SuppressWarnings("unchecked")
		@Override
		public void setAsText(String text) throws IllegalArgumentException {
			setValue(Enum.valueOf(enumType, text));
		}
	}

	static class ActorParameterType extends ParameterType {

		@SuppressWarnings("unchecked")
		@Override
		public <T> T convertFromString(IAppContext context, String arg0) throws Exception {
			PddlModel model = context != null ? context.getService(PddlModel.class) : null;
			return (T) (model != null ? model.Actors.get(arg0) : null);
		}

		@Override
		public String convertToString(IAppContext context, Object arg0) {
			return arg0 instanceof PddlModel.Actor ? ((PddlModel.Actor) arg0).getName() : "";
		}

		@SuppressWarnings("unchecked")
		@Override
		public Class getBaseType() {
			return PddlModel.Actor.class;
		}
	}

	static class TimeParameterType extends ParameterType {

		@SuppressWarnings("unchecked")
		@Override
		public <T> T convertFromString(IAppContext context, String arg0) throws Exception {
			Object result = null;
			try {
				result = Integer.parseInt(arg0);
			} catch (Exception ex) {
				IEventInstanceProvider ev = context != null ? context.getService(IEventInstanceProvider.class) : null;
				result = (ev != null ? ev.getEvent(arg0) : null);
			}
			return (T) result;
		}

		@Override
		public String convertToString(IAppContext context, Object arg0) {
			return arg0 instanceof JEventInstance ? ((JEventInstance) arg0).getID() : arg0 != null ? arg0.toString() : "";
		}

		@Override
		public void createSubProperties(JEventInstanceProperty prop) {
			prop.addSubProperty(new JEventInstance.SubProperty("AbsTime", "Time", int.class));
			prop.addSubProperty(new JEventInstance.SubProperty("Type", "Type", EventTimeType.class));
			prop.addSubProperty(new JEventInstance.SubProperty("Actor", "Actor", PddlModel.Actor.class));
			prop.addSubProperty(new JEventInstance.SubProperty("Action", "Action", EventTimeAction.class));
			prop.addSubProperty(new JEventInstance.SubProperty("Goal", "Goal", PddlModel.Goal.class));
		}
		
		@SuppressWarnings("unchecked")
		@Override
		public Class getBaseType() {
			return EventTime.class;
		}
		
		
	}

	static class ReplanOptionParameterType extends ParameterType {

		@SuppressWarnings("unchecked")
		@Override
		public Class getBaseType() {
			return EventReplanOption.class;
		}
		
		@Override
		public void createSubProperties(JEventInstanceProperty prop) {
			prop.addSubProperty(new JEventInstance.SubProperty("Replan", "Replan", boolean.class));
			prop.addSubProperty(new JEventInstance.SubProperty("NumberOfSolutions", "Max. branches", int.class));
			prop.addSubProperty(new JEventInstance.SubProperty("MaxProcessTime", "Max. time", int.class));
			prop.addSubProperty(new JEventInstance.SubProperty("OnceTimeEvent", "Once-time Event", boolean.class));
		}

	}
	
	public abstract String getName();

	public final NamedList<Parameter> Parameters = new NamedList<Parameter>();

	public JEventType() {
		Parameters.add(new Parameter(TIME, "Happen at", ParameterType.Time));
		Parameters.add(new Parameter(REPLAN_OPT, "Replan Options", ParameterType.ReplanOption));
	}

	public String getDescription() {
		return "";
	}

	public boolean react(IAppContext context, PddlModel model, JEventInstance instance, SolutionSimulator simulator) {
		return false;
	}

	@Override
	public String toString() {
		return getName();
	}
}
